const os = require("os");
// commonjs 模块
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const WorkboxPlugin = require('workbox-webpack-plugin');


const path = require("path");
const threads = os.cpus().length || 1; // cpu 数量
// 用来获取处理样式的loader
function getStyleLoader(pre) {
  return [
    //  执行顺序：从右到左，从下到上
    // "style-loader", // 将js中的css通过创建style标签添加到html文件中生效
    MiniCssExtractPlugin.loader,
    "css-loader",  //  将css资源编译成 commomjs 的模块到js中
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: [
            [
              'postcss-preset-env',
              {
                // 其他选项
              },
            ],
          ],
        },
      },
    },
    pre
  ].filter(Boolean);
}

module.exports = {
  // 入口
  entry: "./src/main.js",
  // 输出
  output: {
    // 绝对路径
    path: path.resolve(__dirname, "../dist"),
    // 入口文件打包输出文件名
    filename: "js/[name].js",
    // 给打包输出的其他文件名
    chunkFilename: "static/js/[name].chunk.js",
    
    // hash 唯一标识 ext：原有文件扩展名
    // 图片、字体等通过 type:asset 处理的资源命名
    assetModuleFilename:'static/image/[hash:5][ext][query]',
    // 自动清空上次打包结果，原理是在打包前将path整个目录清空再进行打包
    clean: true,
  },
  // 加载器
  module: {
    rules: [
      // loader 配置
      {
        oneOf: [
          {
            test: /\.css$/i, // 只检测.css文件
            use: getStyleLoader(),
          },
          {
            test: /\.less$/i,
            use: getStyleLoader('less-loader'),
          },
          {
            test: /\.s[ac]ss$/i,
            use: getStyleLoader('sass-loader'),
          },
          {
            test: /\.styl$/,
            use: getStyleLoader('stylus-loader'),
          },
          {
            test: /\.(png|jpe?g|svg|webp)$/,
            type: "asset",
            parser: {
              dataUrlCondition: {
                // 小于19kb 的图片转base64
                // base64优点：减少请求数量，缺点：体积会增大1/3
                maxSize: 19 * 1024, // 4kb,视为 inline 模块｜resource 模块的界限
              }
            },
            // Rule.generator.filename 与 output.assetModuleFilename 相同，并且仅适用于 asset 和 asset/resource 模块类型。
            // generator: {
            //   filename: 'static/image/[hash][ext][query]'
            // }
          },
          {
            // 资源 resource 输出
            test: /\.(ttf|woff2?|mp3|avi)$/,
            type: "asset/resource",
            // 输出名
            // generator: {
            //   filename: 'static/media/[hash:10][ext][query]'
            // }
          },
          {
            test: /\.m?js$/,
            exclude: /(node_modules|bower_components)/,
            include: path.resolve(__dirname, '../src'),
            use: [
              {
                loader: 'thread-loader',
                options: {
                  workers: threads - 1, // 开启多进程和进程数量
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  // presets: ['@babel/preset-env']
                  cacheDirectory: true, // 开启 babel 缓存
                  cacheCompression: false, // 关闭缓存文件压缩
                  plugins: ['@babel/plugin-transform-runtime']  //禁用了 Babel 自动对每个文件的 runtime 注入，减少代码体积
                }
              }
            ]
          }
        ],
      },
    ]
  },
  optimization: {
    // 压缩的操作
    minimizer: [
      // 在 webpack@5 中，你可以使用 `...` 语法来扩展现有的 minimizer（即 `terser-webpack-plugin`），将下一行取消注释
      // `...`,
      new CssMinimizerPlugin(), // 压缩css
      new TerserWebpackPlugin({
        parallel: threads
      })
    ],
    // 代码分割操作
    splitChunks: {
      chunks: "all",
    },
    // 配置缓存时期的文件名，当子文件hash发生变化时不影响其他对其保持引用的文件
    runtimeChunk:{
      name: (entrypoint)=> `runtime~${entrypoint.name}.js`,
    }
  },
  // 插件
  plugins: [
    new HtmlWebpackPlugin({
      // 模版：以public/index.html 文件创建新的html文件
      // 新的html文件的特点：1. 结构与原来一致，2. 自动引入打包输出的资源
      template: path.resolve(__dirname, "../public/index.html")
    }),
    new ESLintPlugin({
      // 检测哪些文件
      context: path.resolve(__dirname, "../src"),
      cache: true,
      cacheLocation: path.resolve(__dirname, "../node_modules/.cache/eslintcache"),
      threads // 开启多进程和进程数量
    }),
    new MiniCssExtractPlugin({
      filename: "static/css/[name].css",
      // chunkFilename: "static/css/[name].chunk.css"
    }),
    new WorkboxPlugin.GenerateSW({
      // 这些选项帮助快速启用 ServiceWorkers
      // 不允许遗留任何“旧的” ServiceWorkers
      clientsClaim: true,
      skipWaiting: true,
    }),
  ],
  // 模式
  mode: "production",
  // 行列映射
  devtool: "source-map",
}